package com.yunchang.spring.visitor.core.controller;

import com.yunchang.spring.visitor.core.handler.exception.IExceptionHandler;
import com.yunchang.spring.visitor.core.handler.request.IRequestHandler;
import com.yunchang.spring.visitor.core.handler.response.IResponseHandler;
import com.yunchang.spring.visitor.core.invoker.IRestfulInvoker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * <pre>
 * 基于namespace,service和id进行反射调用对应IRestfulService方法的基类Controller。
 * 封装了请求处理过程中的异常处理，以及返回格式的统一处理。
 * 请求入参：
 *      namespace 用于区分service
 *      service 特定service类，必须实现IRestfulService接口，且有注解ReflectiveService Service
 *      id 指定的主键id
 * 请求返回：
 *      1.如果反射的service方法正常返回，则会调用IResponseHandler对应的实现类，统一处理返回内容
 *      2.如果反射的service方法抛出异常，则会调用IExceptionHandler对应的实现类，统一处理返回内容
 * 缓存：
 *      1.如果方法的@ReflectiveMethod注解cacheResult为true，表明该方法支持缓存。
 *        则请求同一个service的同一个method(参数完全相同)的情况下，后续几次请求会优先从缓存中查询方法的返回结果。
 *        如果存在则直接返回，如果缓存查询为空，则反射调用对应service方法，并将结果缓存起来。
 *      2.MapServiceCache是默认的缓存实现，支持设定缓存大小，LRU的淘汰策略。
 *        需要自定义缓存类，可以实现IServiceCache接口，并将对应实现类添加进容器配置即可，并将MapServiceCache从容器配置中去掉。
 * </pre>
 * Created by jasontujun on 2018/1/10.
 *
 * @see com.yunchang.spring.visitor.core.annotation.ReflectiveService
 * @see com.yunchang.spring.visitor.core.annotation.ReflectiveMethod
 * @see IResponseHandler
 * @see IExceptionHandler
 */
public abstract class ARestfulController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private IRestfulInvoker defaultInvoker;

    @Resource
    private IExceptionHandler defaultExceptionHandler;

    @Resource
    private IResponseHandler defaultResponseHandler;

    @Resource
    private IRequestHandler defaultRequestHandler;

    /**
     * 获取IServiceInvoker实现类。
     * 子类可以覆盖此方法，替换成自定义的IServiceInvoker实现类。
     *
     * @return 返回IServiceInvoker实现类
     */
    protected IRestfulInvoker getInvoker() {
        return defaultInvoker;
    }


    /**
     * 使用默认的IResponseHandler和IExceptionHandler，反射调用对应service方法。
     * namespace为""  id为""
     *
     * @param request  HttpRequest
     * @param response HttpResponse
     * @param service  service名
     * @return 如果调用正常，返回IResponseHandler处理后的返回结果；如果出现异常，返回IExceptionHandler处理后的返回结果
     */
    protected Object invokeService(final HttpServletRequest request,
                                   final HttpServletResponse response,
                                   final String service) {
        return invokeService(request, response, "", service);
    }

    /**
     * 使用默认的IResponseHandler和IExceptionHandler，反射调用对应service方法。
     * id为""
     *
     * @param request   HttpRequest
     * @param namespace namespace
     * @param response  HttpResponse
     * @param service   service名
     * @return 如果调用正常，返回IResponseHandler处理后的返回结果；如果出现异常，返回IExceptionHandler处理后的返回结果
     */
    protected Object invokeService(final HttpServletRequest request,
                                   final HttpServletResponse response,
                                   final String namespace,
                                   final String service) {
        return invokeService(request, response, namespace, service, "");
    }


    /**
     * 使用默认的IResponseHandler和IExceptionHandler，反射调用对应service方法。
     *
     * @param request   HttpRequest
     * @param response  HttpResponse
     * @param namespace namespace
     * @param service   service名
     * @param id        id
     * @return 如果调用正常，返回IResponseHandler处理后的返回结果；如果出现异常，返回IExceptionHandler处理后的返回结果
     */
    protected Object invokeService(final HttpServletRequest request,
                                   final HttpServletResponse response,
                                   final String namespace,
                                   final String service,
                                   final String id) {
        return invokeService(request, response, namespace, service, id,
                defaultResponseHandler);
    }

    /**
     * 使用自定义的IResponseHandler，默认的IExceptionHandler，反射调用对应service方法。
     *
     * @param request         HttpRequest
     * @param response        HttpResponse
     * @param namespace       namespace
     * @param service         service名
     * @param id              id
     * @param responseHandler 自定义IResponseHandler
     * @return 如果调用正常，返回IResponseHandler处理后的返回结果；如果出现异常，返回IExceptionHandler处理后的返回结果
     */
    protected Object invokeService(final HttpServletRequest request,
                                   final HttpServletResponse response,
                                   final String namespace,
                                   final String service,
                                   final String id,
                                   final IResponseHandler responseHandler) {
        return invokeService(request, response, namespace, service, id,
                responseHandler, defaultExceptionHandler);
    }

    /**
     * 使用默认的IResponseHandler和IExceptionHandler，反射调用对应service方法。
     *
     * @param request          HttpRequest
     * @param response         HttpResponse
     * @param namespace        namespace
     * @param service          servicename
     * @param id               查找的id
     * @param responseHandler  自定义IResponseHandler
     * @param exceptionHandler 自定义IExceptionHandler
     * @return 如果调用正常，返回IResponseHandler处理后的返回结果；如果出现异常，返回IExceptionHandler处理后的返回结果
     */
    protected Object invokeService(final HttpServletRequest request,
                                   final HttpServletResponse response,
                                   final String namespace,
                                   final String service,
                                   final String id,
                                   final IResponseHandler responseHandler,
                                   final IExceptionHandler exceptionHandler) {
        final long startTime = System.currentTimeMillis();
        Map<String, String> paramMap = null;
        try {
            paramMap = defaultRequestHandler.getParams(request);
            Object result = getInvoker().invokeRestful(namespace, service, id, request, paramMap);
            return responseHandler.handleResponse(request, response,
                    service, id, System.currentTimeMillis() - startTime, result, paramMap, null);
        } catch (Throwable e) {
            logger.error("Error in namespace:" + namespace + ",service:" + service + ",id:" + id, e);
            return exceptionHandler.handleError(request, response,
                    System.currentTimeMillis() - startTime, e, paramMap, null);
        }
    }
}
